

<!DOCTYPE html>
<html lang="zh-CN" data-default-color-scheme=auto>



<head>
  <meta charset="UTF-8">
  <link rel="apple-touch-icon" sizes="76x76" href="/img/favicon.png">
  <link rel="icon" href="/img/favicon.png">
  <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=5.0, shrink-to-fit=no">
  <meta http-equiv="x-ua-compatible" content="ie=edge">
  
  <meta name="theme-color" content="#2f4154">
  <meta name="description" content="Blue~u~u~u">
  <meta name="author" content="Blue~u~u~u">
  <meta name="keywords" content="">
  <meta name="description" content="分布式锁优化过程、Redisson，AOP实现缓存一  分布式锁优化过程1 本地锁的局限性我们学习过synchronized及lock锁，这些锁都是本地锁。接下来写一个案例，演示本地锁的问题 1.1 编写测试代码在TestController中添加测试方法 1234567public interface RedisTestService &amp;#123;    &#x2F;**     * redis的锁测试d">
<meta property="og:type" content="article">
<meta property="og:title" content="redis-分布式锁">
<meta property="og:url" content="http://www.slx.blue/2022/03/23/redis-%E5%88%86%E5%B8%83%E5%BC%8F%E9%94%81/index.html">
<meta property="og:site_name" content="Blue~u~u~u~u">
<meta property="og:description" content="分布式锁优化过程、Redisson，AOP实现缓存一  分布式锁优化过程1 本地锁的局限性我们学习过synchronized及lock锁，这些锁都是本地锁。接下来写一个案例，演示本地锁的问题 1.1 编写测试代码在TestController中添加测试方法 1234567public interface RedisTestService &amp;#123;    &#x2F;**     * redis的锁测试d">
<meta property="og:locale" content="zh_CN">
<meta property="og:image" content="https://lllong.oss-cn-shenzhen.aliyuncs.com/redis/%E5%88%86%E5%B8%83%E5%BC%8F%E9%94%81/clip_image006.jpg">
<meta property="og:image" content="https://lllong.oss-cn-shenzhen.aliyuncs.com/redis/%E5%88%86%E5%B8%83%E5%BC%8F%E9%94%81/clip_image008.png">
<meta property="og:image" content="https://lllong.oss-cn-shenzhen.aliyuncs.com/redis/%E5%88%86%E5%B8%83%E5%BC%8F%E9%94%81/clip_image010.jpg">
<meta property="og:image" content="https://lllong.oss-cn-shenzhen.aliyuncs.com/redis/%E5%88%86%E5%B8%83%E5%BC%8F%E9%94%81/clip_image012.jpg">
<meta property="og:image" content="https://lllong.oss-cn-shenzhen.aliyuncs.com/redis/%E5%88%86%E5%B8%83%E5%BC%8F%E9%94%81/clip_image014.jpg">
<meta property="og:image" content="https://lllong.oss-cn-shenzhen.aliyuncs.com/redis/%E5%88%86%E5%B8%83%E5%BC%8F%E9%94%81/clip_image016.jpg">
<meta property="og:image" content="https://lllong.oss-cn-shenzhen.aliyuncs.com/redis/%E5%88%86%E5%B8%83%E5%BC%8F%E9%94%81/clip_image018.jpg">
<meta property="og:image" content="https://lllong.oss-cn-shenzhen.aliyuncs.com/redis/%E5%88%86%E5%B8%83%E5%BC%8F%E9%94%81/clip_image019.png">
<meta property="og:image" content="https://lllong.oss-cn-shenzhen.aliyuncs.com/redis/%E5%88%86%E5%B8%83%E5%BC%8F%E9%94%81/clip_image022.jpg">
<meta property="og:image" content="https://lllong.oss-cn-shenzhen.aliyuncs.com/redis/%E5%88%86%E5%B8%83%E5%BC%8F%E9%94%81/clip_image024.gif">
<meta property="og:image" content="https://lllong.oss-cn-shenzhen.aliyuncs.com/redis/%E5%88%86%E5%B8%83%E5%BC%8F%E9%94%81/clip_image026.gif">
<meta property="og:image" content="https://lllong.oss-cn-shenzhen.aliyuncs.com/redis/%E5%88%86%E5%B8%83%E5%BC%8F%E9%94%81/clip_image028.gif">
<meta property="og:image" content="https://lllong.oss-cn-shenzhen.aliyuncs.com/redis/%E5%88%86%E5%B8%83%E5%BC%8F%E9%94%81/clip_image030.gif">
<meta property="og:image" content="https://lllong.oss-cn-shenzhen.aliyuncs.com/redis/%E5%88%86%E5%B8%83%E5%BC%8F%E9%94%81/clip_image032.jpg">
<meta property="og:image" content="https://lllong.oss-cn-shenzhen.aliyuncs.com/redis/%E5%88%86%E5%B8%83%E5%BC%8F%E9%94%81/clip_image034.jpg">
<meta property="og:image" content="https://lllong.oss-cn-shenzhen.aliyuncs.com/redis/%E5%88%86%E5%B8%83%E5%BC%8F%E9%94%81/clip_image036.jpg">
<meta property="og:image" content="https://lllong.oss-cn-shenzhen.aliyuncs.com/redis/%E5%88%86%E5%B8%83%E5%BC%8F%E9%94%81/clip_image036.jpg">
<meta property="og:image" content="https://lllong.oss-cn-shenzhen.aliyuncs.com/redis/%E5%88%86%E5%B8%83%E5%BC%8F%E9%94%81/clip_image038.jpg">
<meta property="og:image" content="https://lllong.oss-cn-shenzhen.aliyuncs.com/redis/%E5%88%86%E5%B8%83%E5%BC%8F%E9%94%81/clip_image042.jpg">
<meta property="og:image" content="https://lllong.oss-cn-shenzhen.aliyuncs.com/redis/%E5%88%86%E5%B8%83%E5%BC%8F%E9%94%81/clip_image043.png">
<meta property="og:image" content="https://lllong.oss-cn-shenzhen.aliyuncs.com/redis/%E5%88%86%E5%B8%83%E5%BC%8F%E9%94%81/clip_image047.png">
<meta property="og:image" content="https://lllong.oss-cn-shenzhen.aliyuncs.com/redis/%E5%88%86%E5%B8%83%E5%BC%8F%E9%94%81/clip_image050.jpg">
<meta property="og:image" content="https://lllong.oss-cn-shenzhen.aliyuncs.com/redis/%E5%88%86%E5%B8%83%E5%BC%8F%E9%94%81/clip_image052.jpg">
<meta property="og:image" content="https://lllong.oss-cn-shenzhen.aliyuncs.com/redis/%E5%88%86%E5%B8%83%E5%BC%8F%E9%94%81/clip_image054.jpg">
<meta property="article:published_time" content="2022-03-23T12:54:19.000Z">
<meta property="article:modified_time" content="2022-03-23T12:55:48.219Z">
<meta property="article:author" content="Blue~u~u~u">
<meta property="article:tag" content="redis">
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:image" content="https://lllong.oss-cn-shenzhen.aliyuncs.com/redis/%E5%88%86%E5%B8%83%E5%BC%8F%E9%94%81/clip_image006.jpg">
  
  <title>redis-分布式锁 - Blue~u~u~u~u</title>

  <link  rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4/dist/css/bootstrap.min.css" />


  <link  rel="stylesheet" href="https://cdn.jsdelivr.net/npm/github-markdown-css@4/github-markdown.min.css" />
  <link  rel="stylesheet" href="/lib/hint/hint.min.css" />

  
    
    
      
      <link  rel="stylesheet" href="https://cdn.jsdelivr.net/npm/highlight.js@10/styles/github-gist.min.css" />
    
  

  
    <link  rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@fancyapps/fancybox@3/dist/jquery.fancybox.min.css" />
  


<!-- 主题依赖的图标库，不要自行修改 -->

<link rel="stylesheet" href="//at.alicdn.com/t/font_1749284_ba1fz6golrf.css">



<link rel="stylesheet" href="//at.alicdn.com/t/font_1736178_kmeydafke9r.css">


<link  rel="stylesheet" href="/css/main.css" />

<!-- 自定义样式保持在最底部 -->


  <script id="fluid-configs">
    var Fluid = window.Fluid || {};
    var CONFIG = {"hostname":"www.slx.blue","root":"/","version":"1.8.12","typing":{"enable":true,"typeSpeed":180,"cursorChar":"_","loop":true},"anchorjs":{"enable":true,"element":"h1,h2,h3,h4,h5,h6","placement":"right","visible":"hover","icon":""},"progressbar":{"enable":true,"height_px":3,"color":"#29d","options":{"showSpinner":false,"trickleSpeed":100}},"copy_btn":true,"image_zoom":{"enable":true,"img_url_replace":["",""]},"toc":{"enable":true,"headingSelector":"h1,h2,h3,h4,h5,h6","collapseDepth":0},"lazyload":{"enable":true,"loading_img":"/img/loading.gif","onlypost":false,"offset_factor":2},"web_analytics":{"enable":false,"baidu":null,"google":null,"gtag":null,"tencent":{"sid":null,"cid":null},"woyaola":null,"cnzz":null,"leancloud":{"app_id":null,"app_key":null,"server_url":null,"path":"window.location.pathname"}},"search_path":"/local-search.xml"};
  </script>
  <script  src="/js/utils.js" ></script>
  <script  src="/js/color-schema.js" ></script>
<meta name="generator" content="Hexo 5.4.0"></head>

<script src="https://cdn.jsdelivr.net/npm/jquery/dist/jquery.min.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/font-awesome/css/font-awesome.min.css"/>
<script src="/live2d-widget/autoload.js"></script>


<body>
  <header style="height: 70vh;">
    <nav id="navbar" class="navbar fixed-top  navbar-expand-lg navbar-dark scrolling-navbar">
  <div class="container">
    <a class="navbar-brand" href="/">
      <strong>Blue~u~u</strong>
    </a>

    <button id="navbar-toggler-btn" class="navbar-toggler" type="button" data-toggle="collapse"
            data-target="#navbarSupportedContent"
            aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
      <div class="animated-icon"><span></span><span></span><span></span></div>
    </button>

    <!-- Collapsible content -->
    <div class="collapse navbar-collapse" id="navbarSupportedContent">
      <ul class="navbar-nav ml-auto text-center">
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/">
                <i class="iconfont icon-home-fill"></i>
                首页
              </a>
            </li>
          
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/archives/">
                <i class="iconfont icon-archive-fill"></i>
                归档
              </a>
            </li>
          
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/categories/">
                <i class="iconfont icon-category-fill"></i>
                分类
              </a>
            </li>
          
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/tags/">
                <i class="iconfont icon-tags-fill"></i>
                标签
              </a>
            </li>
          
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/about/">
                <i class="iconfont icon-user-fill"></i>
                关于
              </a>
            </li>
          
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/links/">
                <i class="iconfont icon-link-fill"></i>
                友链
              </a>
            </li>
          
        
        
          <li class="nav-item" id="search-btn">
            <a class="nav-link" target="_self" href="javascript:;" data-toggle="modal" data-target="#modalSearch" aria-label="Search">
              &nbsp;<i class="iconfont icon-search"></i>&nbsp;
            </a>
          </li>
        
        
          <li class="nav-item" id="color-toggle-btn">
            <a class="nav-link" target="_self" href="javascript:;" aria-label="Color Toggle">&nbsp;<i
                class="iconfont icon-dark" id="color-toggle-icon"></i>&nbsp;</a>
          </li>
        
      </ul>
    </div>
  </div>
</nav>

    <div class="banner" id="banner" parallax=true
         style="background: url('/images/default.png') no-repeat center center;
           background-size: cover;">
      <div class="full-bg-img">
        <div class="mask flex-center" style="background-color: rgba(0, 0, 0, 0.3)">
          <div class="page-header text-center fade-in-up">
            <span class="h2" id="subtitle" title="redis-分布式锁">
              
            </span>

            
              <div class="mt-3">
  
  
    <span class="post-meta">
      <i class="iconfont icon-date-fill" aria-hidden="true"></i>
      <time datetime="2022-03-23 20:54" pubdate>
        2022年3月23日 晚上
      </time>
    </span>
  
</div>

<div class="mt-1">
  
    <span class="post-meta mr-2">
      <i class="iconfont icon-chart"></i>
      21k 字
    </span>
  

  
    <span class="post-meta mr-2">
      <i class="iconfont icon-clock-fill"></i>
      
      
      65 分钟
    </span>
  

  
  
    
      <!-- 不蒜子统计文章PV -->
      <span id="busuanzi_container_page_pv" style="display: none">
        <i class="iconfont icon-eye" aria-hidden="true"></i>
        <span id="busuanzi_value_page_pv"></span> 次
      </span>
    
  
</div>

            
          </div>

          
        </div>
      </div>
    </div>
  </header>

  <main>
    
      

<div class="container-fluid nopadding-x">
  <div class="row nomargin-x">
    <div class="d-none d-lg-block col-lg-2"></div>
    <div class="col-lg-8 nopadding-x-md">
      <div class="container nopadding-x-md" id="board-ctn">
        <div class="py-5" id="board">
          <article class="post-content mx-auto">
            <!-- SEO header -->
            <h1 style="display: none">redis-分布式锁</h1>
            
              <p class="note note-info">
                
                  本文最后更新于：2 个月前
                
              </p>
            
            <div class="markdown-body">
              <h1 id="分布式锁优化过程、Redisson，AOP实现缓存"><a href="#分布式锁优化过程、Redisson，AOP实现缓存" class="headerlink" title="分布式锁优化过程、Redisson，AOP实现缓存"></a>分布式锁优化过程、Redisson，AOP实现缓存</h1><h1 id="一-分布式锁优化过程"><a href="#一-分布式锁优化过程" class="headerlink" title="一  分布式锁优化过程"></a>一  分布式锁优化过程</h1><h2 id="1-本地锁的局限性"><a href="#1-本地锁的局限性" class="headerlink" title="1 本地锁的局限性"></a>1 本地锁的局限性</h2><p>我们学习过synchronized及lock锁，这些锁都是本地锁。接下来写一个案例，演示本地锁的问题</p>
<h3 id="1-1-编写测试代码"><a href="#1-1-编写测试代码" class="headerlink" title="1.1 编写测试代码"></a>1.1 编写测试代码</h3><p>在TestController中添加测试方法</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">RedisTestService</span> </span>&#123;<br>    <span class="hljs-comment">/**</span><br><span class="hljs-comment">     * redis的锁测试demo1</span><br><span class="hljs-comment">     */</span><br>    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">lockDemo1</span><span class="hljs-params">()</span></span>;<br>&#125;<br><br></code></pre></td></tr></table></figure>

<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Service</span><br><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">RedisTestServiceImpl</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">RedisTestService</span> </span>&#123;<br><br>    <span class="hljs-meta">@Autowired</span><br>    <span class="hljs-keyword">private</span> StringRedisTemplate stringRedisTemplate;<br><br>    <span class="hljs-comment">/**</span><br><span class="hljs-comment">     * redis的锁测试demo1</span><br><span class="hljs-comment">     */</span><br>    <span class="hljs-meta">@Override</span><br>    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">lockDemo1</span><span class="hljs-params">()</span> </span>&#123;<br>        <span class="hljs-comment">//从缓存中获取key</span><br>        String s = stringRedisTemplate.boundValueOps(<span class="hljs-string">&quot;demo1_num&quot;</span>).get();<br>        <span class="hljs-comment">//判断是否为空</span><br>        <span class="hljs-keyword">if</span>(StringUtils.isEmpty(s))&#123;<br>            <span class="hljs-keyword">return</span>;<br>        &#125;<br>        <span class="hljs-comment">//若不为空</span><br>        Integer num = Integer.parseInt(s);<br>        <span class="hljs-comment">//自增保存</span><br>        stringRedisTemplate.boundValueOps(<span class="hljs-string">&quot;demo1_num&quot;</span>).set(++num + <span class="hljs-string">&quot;&quot;</span>);<br>    &#125;<br>&#125;<br><br></code></pre></td></tr></table></figure>

<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@RestController</span><br><span class="hljs-meta">@RequestMapping(value = &quot;/redis/product/test&quot;)</span><br><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">RedisTestController</span> </span>&#123;<br><br>    <span class="hljs-meta">@Autowired</span><br>    <span class="hljs-keyword">private</span> RedisTestService redisTestService;<br><br>    <span class="hljs-meta">@GetMapping(value = &quot;/demo1&quot;)</span><br>    <span class="hljs-function"><span class="hljs-keyword">public</span> Result <span class="hljs-title">demo1</span><span class="hljs-params">()</span></span>&#123;<br>        redisTestService.lockDemo1();<br>        <span class="hljs-keyword">return</span> Result.ok();<br>    &#125;<br>&#125;<br><br></code></pre></td></tr></table></figure>

<p>说明：通过reids客户端设置<strong>num</strong>=0</p>
<h3 id="1-2-使用ab工具测试"><a href="#1-2-使用ab工具测试" class="headerlink" title="1.2 使用ab工具测试"></a>1.2 使用ab工具测试</h3><p>在redis中，玩ab测试工具：httpd-tools（yum install -y httpd-tools）</p>
<figure class="highlight apache"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs apache"><span class="hljs-attribute">ab</span>  -n（一次发送的请求数）  -c（请求的并发数） 访问路径<br><span class="hljs-attribute">ab</span> -n <span class="hljs-number">5000</span> -c <span class="hljs-number">100</span> http://<span class="hljs-number">192.168.200.1:8206</span>/api/redis/test<span class="hljs-number">01</span><br></code></pre></td></tr></table></figure>

<p>测试如下：5000请求，100并发</p>
<p><img src="https://lllong.oss-cn-shenzhen.aliyuncs.com/redis/%E5%88%86%E5%B8%83%E5%BC%8F%E9%94%81/clip_image006.jpg" srcset="/img/loading.gif" lazyload alt="img"></p>
<p>查看redis中的值：</p>
<p><img src="https://lllong.oss-cn-shenzhen.aliyuncs.com/redis/%E5%88%86%E5%B8%83%E5%BC%8F%E9%94%81/clip_image008.png" srcset="/img/loading.gif" lazyload alt="image-20220323151354704"></p>
<h3 id="1-3-使用本地锁"><a href="#1-3-使用本地锁" class="headerlink" title="1.3 使用本地锁"></a>1.3 使用本地锁</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Service</span><br><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">RedisTestServiceImpl</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">RedisTestService</span> </span>&#123;<br><br>    <span class="hljs-meta">@Autowired</span><br>    <span class="hljs-keyword">private</span> StringRedisTemplate stringRedisTemplate;<br>    <span class="hljs-comment">/**</span><br><span class="hljs-comment">     * redis的锁测试demo1</span><br><span class="hljs-comment">     */</span><br>    <span class="hljs-meta">@Override</span><br>    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">synchronized</span> <span class="hljs-keyword">void</span> <span class="hljs-title">lockDemo1</span><span class="hljs-params">()</span> </span>&#123;<br>        <span class="hljs-comment">//从缓存中获取key</span><br>        String s = stringRedisTemplate.boundValueOps(<span class="hljs-string">&quot;demo1_num&quot;</span>).get();<br>        <span class="hljs-comment">//判断是否为空</span><br>        <span class="hljs-keyword">if</span>(StringUtils.isEmpty(s))&#123;<br>            <span class="hljs-keyword">return</span>;<br>        &#125;<br>        <span class="hljs-comment">//若不为空</span><br>        Integer num = Integer.parseInt(s);<br>        <span class="hljs-comment">//自增保存</span><br>        stringRedisTemplate.boundValueOps(<span class="hljs-string">&quot;demo1_num&quot;</span>).set(++num + <span class="hljs-string">&quot;&quot;</span>);<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>

<p>使用ab工具压力测试：5000次请求，并发100</p>
<p><img src="https://lllong.oss-cn-shenzhen.aliyuncs.com/redis/%E5%88%86%E5%B8%83%E5%BC%8F%E9%94%81/clip_image010.jpg" srcset="/img/loading.gif" lazyload alt="img"></p>
<p>查看redis中的结果：</p>
<p><img src="https://lllong.oss-cn-shenzhen.aliyuncs.com/redis/%E5%88%86%E5%B8%83%E5%BC%8F%E9%94%81/clip_image012.jpg" srcset="/img/loading.gif" lazyload alt="img"></p>
<p>完美！与预期一致，是否真的完美？</p>
<p>接下来再看集群情况下，会怎样？</p>
<h3 id="1-4-本地锁问题演示锁"><a href="#1-4-本地锁问题演示锁" class="headerlink" title="1.4 本地锁问题演示锁"></a>1.4 本地锁问题演示锁</h3><p>接下来启动8206 8216 8226 三个运行实例。</p>
<p>运行多个service-product实例：</p>
<p><img src="https://lllong.oss-cn-shenzhen.aliyuncs.com/redis/%E5%88%86%E5%B8%83%E5%BC%8F%E9%94%81/clip_image014.jpg" srcset="/img/loading.gif" lazyload alt="image-20220323152243090"></p>
<p><strong>通过网关压力测试</strong>：</p>
<p>启动网关：</p>
<p>ab -n 5000 -c 100 <a target="_blank" rel="noopener" href="http://192.168.200.1/redis/product/test/testLock">http://192.168.200.1/redis/product/test/testLock</a></p>
<p><img src="https://lllong.oss-cn-shenzhen.aliyuncs.com/redis/%E5%88%86%E5%B8%83%E5%BC%8F%E9%94%81/clip_image016.jpg" srcset="/img/loading.gif" lazyload alt="img"></p>
<p>查看redis中的值：</p>
<p><img src="https://lllong.oss-cn-shenzhen.aliyuncs.com/redis/%E5%88%86%E5%B8%83%E5%BC%8F%E9%94%81/clip_image018.jpg" srcset="/img/loading.gif" lazyload alt="img"></p>
<p>集群情况下又出问题了！！！</p>
<p>以上测试，可以发现：</p>
<p> 本地锁只能锁住同一工程内的资源，在分布式系统里面都存在局限性。</p>
<p>此时需要分布式锁</p>
<h2 id="2-分布式锁实现"><a href="#2-分布式锁实现" class="headerlink" title="2 分布式锁实现"></a>2 分布式锁实现</h2><p>随着业务发展的需要，原单体单机部署的系统被演化成分布式集群系统后，由于分布式系统多线程、多进程并且分布在不同机器上，这将使原单机部署情况下的并发控制锁策略失效，单纯的Java API并不能提供分布式锁的能力。为了解决这个问题就需要一种跨JVM的互斥机制来控制共享资源的访问，这就是分布式锁要解决的问题！</p>
<p><strong>分布式锁主流的实现方案：</strong></p>
<ol>
<li><p>基于数据库实现分布式锁</p>
</li>
<li><p>基于缓存（Redis等）</p>
</li>
<li><p>基于Zookeeper</p>
</li>
</ol>
<p><strong>每一种分布式锁解决方案都有各自的优缺点：</strong></p>
<ol>
<li><p>性能：redis最高</p>
</li>
<li><p>可靠性：zookeeper最高</p>
</li>
</ol>
<p>==这里，我们就基于redis实现分布式锁。==</p>
<h2 id="3-使用redis实现分布式锁"><a href="#3-使用redis实现分布式锁" class="headerlink" title="3 使用redis实现分布式锁"></a>3 使用redis实现分布式锁</h2><p>redis命令</p>
<p>set sku:1:info “OK” NX PX 10000</p>
<p>EX second ：设置键的过期时间为 second 秒。 SET key value EX second 效果等同于 SETEX key second value 。</p>
<p>PX millisecond ：设置键的过期时间为 millisecond 毫秒。 SET key value PX millisecond 效果等同于 SETEX key millisecond value 。</p>
<p>NX ：只在键不存在时，才对键进行设置操作。 SET key value NX 效果等同于 SETNX key value 。</p>
<p>XX ：只在键已经存在时，才对键进行设置操作。</p>
<p><img src="https://lllong.oss-cn-shenzhen.aliyuncs.com/redis/%E5%88%86%E5%B8%83%E5%BC%8F%E9%94%81/clip_image019.png" srcset="/img/loading.gif" lazyload alt="1568170959121"></p>
<ol>
<li><p>多个客户端同时获取锁（setnx）</p>
</li>
<li><p>获取成功，执行业务逻辑，执行完成释放锁（del）</p>
</li>
<li><p>其他客户端等待重试</p>
</li>
</ol>
<h3 id="3-1-编写代码"><a href="#3-1-编写代码" class="headerlink" title="3.1 编写代码"></a>3.1 编写代码</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Service</span><br><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">RedisTestServiceImpl</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">RedisTestService</span> </span>&#123;<br><br>    <span class="hljs-meta">@Autowired</span><br>    <span class="hljs-keyword">private</span> StringRedisTemplate stringRedisTemplate;<br><br>    <span class="hljs-comment">/**</span><br><span class="hljs-comment">     * redis的锁测试demo1</span><br><span class="hljs-comment">     */</span><br>    <span class="hljs-meta">@Override</span><br>    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">lockDemo1</span><span class="hljs-params">()</span> </span>&#123;<br>        Boolean lock = stringRedisTemplate.opsForValue().setIfAbsent(<span class="hljs-string">&quot;lock&quot;</span>, <span class="hljs-string">&quot;111&quot;</span>);<br>        <span class="hljs-comment">//lock为true的时候代表获取到了锁</span><br>        <span class="hljs-keyword">if</span>(lock)&#123;<br>            <span class="hljs-comment">//从缓存中获取key</span><br>            String s = stringRedisTemplate.boundValueOps(<span class="hljs-string">&quot;demo1_num&quot;</span>).get();<br>            <span class="hljs-comment">//判断是否为空</span><br>            <span class="hljs-keyword">if</span>(StringUtils.isEmpty(s))&#123;<br>                <span class="hljs-keyword">return</span>;<br>            &#125;<br>            <span class="hljs-comment">//若不为空</span><br>            Integer num = Integer.parseInt(s);<br>            <span class="hljs-comment">//自增保存</span><br>            stringRedisTemplate.boundValueOps(<span class="hljs-string">&quot;demo1_num&quot;</span>).set(++num + <span class="hljs-string">&quot;&quot;</span>);<br>            <span class="hljs-comment">//释放锁</span><br>            stringRedisTemplate.delete(<span class="hljs-string">&quot;lock&quot;</span>);<br>        &#125;<span class="hljs-keyword">else</span>&#123;<br>            <span class="hljs-keyword">try</span> &#123;<br>                <span class="hljs-comment">//没有获取到锁,线程睡觉1秒</span><br>                Thread.sleep(<span class="hljs-number">1000</span>);<br>                <span class="hljs-comment">//重试:递归</span><br>                lockDemo1();<br>            &#125;<span class="hljs-keyword">catch</span> (Exception e)&#123;<br>                e.printStackTrace();<br>            &#125;<br>        &#125;<br><br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>

<p>重启，服务集群，通过网关压力测试：</p>
<p><img src="https://lllong.oss-cn-shenzhen.aliyuncs.com/redis/%E5%88%86%E5%B8%83%E5%BC%8F%E9%94%81/clip_image022.jpg" srcset="/img/loading.gif" lazyload alt="img"></p>
<p>查看redis中num的值：</p>
<p><img src="https://lllong.oss-cn-shenzhen.aliyuncs.com/redis/%E5%88%86%E5%B8%83%E5%BC%8F%E9%94%81/clip_image024.gif" srcset="/img/loading.gif" lazyload alt="1568172286652"></p>
<p>基本实现。</p>
<p><strong>问题：setnx刚好获取到锁，业务逻辑出现异常，导致锁无法释放</strong></p>
<p>==解决：设置过期时间，自动释放锁。==</p>
<h3 id="3-2-优化之设置锁的过期时间"><a href="#3-2-优化之设置锁的过期时间" class="headerlink" title="3.2 优化之设置锁的过期时间"></a>3.2 优化之设置锁的过期时间</h3><p><strong>设置过期时间有两种方式：</strong></p>
<ol>
<li><p>首先想到通过expire设置过期时间（缺乏原子性：如果在setnx和expire之间出现异常，锁也无法释放）</p>
</li>
<li><p>在set时指定过期时间（推荐）</p>
</li>
</ol>
<p><img src="https://lllong.oss-cn-shenzhen.aliyuncs.com/redis/%E5%88%86%E5%B8%83%E5%BC%8F%E9%94%81/clip_image026.gif" srcset="/img/loading.gif" lazyload alt="1568172928666"></p>
<p>设置过期时间：</p>
<p><img src="https://lllong.oss-cn-shenzhen.aliyuncs.com/redis/%E5%88%86%E5%B8%83%E5%BC%8F%E9%94%81/clip_image028.gif" srcset="/img/loading.gif" lazyload alt="1568173359179"></p>
<p>压力测试肯定也没有问题。自行测试</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs java">Boolean lock = redisTemplate.opsForValue()<br>                .setIfAbsent(<span class="hljs-string">&quot;lock&quot;</span>, <span class="hljs-string">&quot;111&quot;</span>,<span class="hljs-number">3</span>,TimeUnit.SECONDS);<br></code></pre></td></tr></table></figure>



<p><strong>问题：可能会释放其他服务器的锁。</strong></p>
<p>==场景：如果业务逻辑的执行时间是7s。执行流程如下==</p>
<ol>
<li><p>index1业务逻辑没执行完，3秒后锁被自动释放。</p>
</li>
<li><p>index2获取到锁，执行业务逻辑，3秒后锁被自动释放。</p>
</li>
<li><p>index3获取到锁，执行业务逻辑</p>
</li>
<li><p>index1业务逻辑执行完成，开始调用del释放锁，这时释放的是index3的锁，导致index3的业务只执行1s就被别人释放。</p>
</li>
</ol>
<p>最终等于没锁的情况。</p>
<p><strong>解决：setnx获取锁时，设置一个指定的唯一值（例如：uuid）；释放前获取这个值，判断是否自己的锁</strong></p>
<h3 id="3-3-优化之UUID防误删"><a href="#3-3-优化之UUID防误删" class="headerlink" title="3.3 优化之UUID防误删"></a>3.3 优化之UUID防误删</h3><p><img src="https://lllong.oss-cn-shenzhen.aliyuncs.com/redis/%E5%88%86%E5%B8%83%E5%BC%8F%E9%94%81/clip_image030.gif" srcset="/img/loading.gif" lazyload alt="1568174534027"></p>
<p><img src="https://lllong.oss-cn-shenzhen.aliyuncs.com/redis/%E5%88%86%E5%B8%83%E5%BC%8F%E9%94%81/clip_image032.jpg" srcset="/img/loading.gif" lazyload alt="img"></p>
<p>==<strong>问题：删除操作缺乏原子性。</strong>==</p>
<p>场景：</p>
<ol>
<li><p>index1执行删除时，查询到的lock值确实和uuid相等</p>
<p>uuid=v1</p>
<p>set(lock,uuid)；</p>
<p><img src="https://lllong.oss-cn-shenzhen.aliyuncs.com/redis/%E5%88%86%E5%B8%83%E5%BC%8F%E9%94%81/clip_image034.jpg" srcset="/img/loading.gif" lazyload alt="img"></p>
</li>
<li><p>index1执行删除前，lock刚好过期时间已到，被redis自动释放</p>
<p>在redis中没有了lock，没有了锁。</p>
<p><img src="https://lllong.oss-cn-shenzhen.aliyuncs.com/redis/%E5%88%86%E5%B8%83%E5%BC%8F%E9%94%81/clip_image036.jpg" srcset="/img/loading.gif" lazyload alt="img"></p>
</li>
<li><p>index2获取了lock</p>
<p>index2线程获取到了cpu的资源，开始执行方法</p>
<p>uuid=v2</p>
<p>set(lock,uuid)；</p>
</li>
<li><p>index1执行删除，此时会把index2的lock删除</p>
<p>index1 因为已经在方法中了，所以不需要重新上锁。index1有执行的权限。index1已经比较完成了，这个时候，开始执行</p>
<p><img src="https://lllong.oss-cn-shenzhen.aliyuncs.com/redis/%E5%88%86%E5%B8%83%E5%BC%8F%E9%94%81/clip_image036.jpg" srcset="/img/loading.gif" lazyload alt="img"></p>
<p>删除的index2的锁！</p>
</li>
</ol>
<h3 id="3-4-优化之LUA脚本保证删除的原子性"><a href="#3-4-优化之LUA脚本保证删除的原子性" class="headerlink" title="3.4 优化之LUA脚本保证删除的原子性"></a>3.4 优化之LUA脚本保证删除的原子性</h3><p>lua脚本</p>
<figure class="highlight lua"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs lua"><span class="hljs-keyword">if</span> redis.call(<span class="hljs-string">&#x27;get&#x27;</span>, KEYS[<span class="hljs-number">1</span>]) == ARGV[<span class="hljs-number">1</span>] <span class="hljs-keyword">then</span> <br>    <span class="hljs-keyword">return</span> redis.call(<span class="hljs-string">&#x27;del&#x27;</span>, KEYS[<span class="hljs-number">1</span>]) <br><span class="hljs-keyword">else</span> <br>    <span class="hljs-keyword">return</span> <span class="hljs-number">0</span> <br><span class="hljs-keyword">end</span><br></code></pre></td></tr></table></figure>

<p>代码</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">package</span> slx.blue.gmall.product.service.impl;<br><br><span class="hljs-keyword">import</span> slx.blue.gmall.product.service.RedisTestService;<br><span class="hljs-keyword">import</span> org.springframework.beans.factory.annotation.Autowired;<br><span class="hljs-keyword">import</span> org.springframework.data.redis.core.StringRedisTemplate;<br><span class="hljs-keyword">import</span> org.springframework.data.redis.core.script.DefaultRedisScript;<br><span class="hljs-keyword">import</span> org.springframework.stereotype.Service;<br><span class="hljs-keyword">import</span> org.springframework.util.StringUtils;<br><br><span class="hljs-keyword">import</span> java.util.Arrays;<br><span class="hljs-keyword">import</span> java.util.UUID;<br><span class="hljs-keyword">import</span> java.util.concurrent.TimeUnit;<br><br><span class="hljs-meta">@Service</span><br><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">RedisTestServiceImpl</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">RedisTestService</span> </span>&#123;<br><br>    <span class="hljs-meta">@Autowired</span><br>    <span class="hljs-keyword">private</span> StringRedisTemplate stringRedisTemplate;<br><br>    <span class="hljs-comment">/**</span><br><span class="hljs-comment">     * redis的锁测试demo1</span><br><span class="hljs-comment">     */</span><br>    <span class="hljs-meta">@Override</span><br>    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">lockDemo1</span><span class="hljs-params">()</span> </span>&#123;<br>        <span class="hljs-comment">// SETNX 如果key不存在，才对key进行设置操作</span><br>        <span class="hljs-comment">//上锁的时候设置过期时间三秒 防止业务异常无法释放锁</span><br>        <span class="hljs-comment">// 并且设置一个唯一的值UUID，防止因业务执行超时后自动释放锁 又主动释放别人的锁</span><br>        String uuid = UUID.randomUUID().toString();<br>        Boolean lock = stringRedisTemplate.opsForValue().setIfAbsent(<span class="hljs-string">&quot;lock&quot;</span>, uuid, <span class="hljs-number">3</span>, TimeUnit.SECONDS);<br>        <span class="hljs-comment">//lock为true的时候代表获取到了锁</span><br>        <span class="hljs-keyword">if</span>(lock)&#123;<br>            <span class="hljs-comment">//从缓存中获取key</span><br>            String s = stringRedisTemplate.boundValueOps(<span class="hljs-string">&quot;demo1_num&quot;</span>).get();<br>            <span class="hljs-comment">//判断是否为空</span><br>            <span class="hljs-keyword">if</span>(StringUtils.isEmpty(s))&#123;<br>                <span class="hljs-keyword">return</span>;<br>            &#125;<br>            <span class="hljs-comment">//若不为空</span><br>            Integer num = Integer.parseInt(s);<br>            <span class="hljs-comment">//自增保存</span><br>            stringRedisTemplate.boundValueOps(<span class="hljs-string">&quot;demo1_num&quot;</span>).set(++num + <span class="hljs-string">&quot;&quot;</span>);<br>			<span class="hljs-comment">//操作完 拿到锁和当前uuid进行判断 看看是否还是自己的锁 如果是则删除</span><br>             <span class="hljs-comment">// 问题  if里面的流程不是原子性的 还是容易出现问题</span><br><span class="hljs-comment">//            if(stringRedisTemplate.opsForValue().get(&quot;lock&quot;).equals(uuid))&#123;</span><br><span class="hljs-comment">////                stringRedisTemplate.delete(&quot;lock&quot;);</span><br><span class="hljs-comment">//            &#125;</span><br>            <br>            <span class="hljs-comment">//用lua脚本删除</span><br>            <span class="hljs-comment">/**</span><br><span class="hljs-comment">             * if redis.call(&#x27;get&#x27;, KEYS[1]) == ARGV[1] then</span><br><span class="hljs-comment">             *     return redis.call(&#x27;del&#x27;, KEYS[1])</span><br><span class="hljs-comment">             * else</span><br><span class="hljs-comment">             *     return 0</span><br><span class="hljs-comment">             * end</span><br><span class="hljs-comment">             */</span><br>            DefaultRedisScript&lt;Long&gt; script = <span class="hljs-keyword">new</span> DefaultRedisScript();<br>            <span class="hljs-comment">//设置lua脚本</span><br>            script.setScriptText(<span class="hljs-string">&quot;if redis.call(&#x27;get&#x27;, KEYS[1]) == ARGV[1] then return redis.call(&#x27;del&#x27;, KEYS[1]) else return 0 end&quot;</span>);<br>            <span class="hljs-comment">//设置返回类型</span><br>            script.setResultType(Long.class);<br>            <span class="hljs-comment">//执行脚本释放锁</span><br>            stringRedisTemplate.execute(script, Arrays.asList(<span class="hljs-string">&quot;lock&quot;</span>), uuid);<br>        &#125;<span class="hljs-keyword">else</span>&#123;<br>            <span class="hljs-keyword">try</span> &#123;<br>                <span class="hljs-comment">//没有获取到锁,线程睡觉1秒</span><br>                Thread.sleep(<span class="hljs-number">1000</span>);<br>                <span class="hljs-comment">//重试:递归</span><br>                lockDemo1();<br>            &#125;<span class="hljs-keyword">catch</span> (Exception e)&#123;<br>                e.printStackTrace();<br>            &#125;<br>        &#125;<br><br>    &#125;<br>&#125;<br><br></code></pre></td></tr></table></figure>

<p>Lua 脚本详解：</p>
<p><img src="https://lllong.oss-cn-shenzhen.aliyuncs.com/redis/%E5%88%86%E5%B8%83%E5%BC%8F%E9%94%81/clip_image038.jpg" srcset="/img/loading.gif" lazyload alt="img"></p>
<h3 id="3-5-总结"><a href="#3-5-总结" class="headerlink" title="3.5 总结"></a>3.5 总结</h3><ol>
<li>加锁</li>
</ol>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">// 1. 从redis中获取锁,set k1 v1 px 20000 nx</span><br>String uuid = UUID.randomUUID().toString();<br>Boolean lock = <span class="hljs-keyword">this</span>.redisTemplate.opsForValue()<br>      .setIfAbsent(<span class="hljs-string">&quot;lock&quot;</span>, uuid, <span class="hljs-number">2</span>, TimeUnit.SECONDS);<br></code></pre></td></tr></table></figure>

<ol start="2">
<li>使用lua释放锁</li>
</ol>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">// 2. 释放锁 del</span><br>String script = <span class="hljs-string">&quot;if redis.call(&#x27;get&#x27;, KEYS[1]) == ARGV[1] then return redis.call(&#x27;del&#x27;, KEYS[1]) else return 0 end&quot;</span>;<br><span class="hljs-comment">// 设置lua脚本返回的数据类型</span><br>DefaultRedisScript&lt;Long&gt; redisScript = <span class="hljs-keyword">new</span> DefaultRedisScript&lt;&gt;();<br><span class="hljs-comment">// 设置lua脚本返回类型为Long</span><br>redisScript.setResultType(Long.class);<br>redisScript.setScriptText(script);<br>redisTemplate.execute(redisScript, Arrays.asList(<span class="hljs-string">&quot;lock&quot;</span>),uuid);<br></code></pre></td></tr></table></figure>

<ol start="3">
<li>重试</li>
</ol>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs java">Thread.sleep(<span class="hljs-number">1000</span>); <br>testLock();<br></code></pre></td></tr></table></figure>

<p><strong>为了确保分布式锁可用，我们至少要确保锁的实现同时满足以下四个条件：</strong></p>
<ul>
<li><p>==互斥性: 在任意时刻，只有一个客户端能持有锁;==</p>
</li>
<li><p>==不会发生死锁: 即使有一个客户端在持有锁的期间崩溃而没有主动解锁，也能保证后续其他客户端能加锁;==</p>
</li>
<li><p>==解铃还须系铃人: 加锁和解锁必须是同一个客户端，客户端自己不能把别人加的锁给解了;==</p>
</li>
<li><p>==加锁和解锁必须具有原子性;==</p>
</li>
</ul>
<p><strong>redis集群状态下的问题：</strong></p>
<ol>
<li><p>客户端A从master获取到锁</p>
</li>
<li><p>在master将锁同步到slave之前，master宕掉了。</p>
</li>
<li><p>slave节点被晋级为master节点</p>
</li>
<li><p>客户端B取得了同一个资源被客户端A已经获取到的另外一个锁。</p>
</li>
</ol>
<p>安全失效！</p>
<p>解决方案：</p>
<p><img src="https://lllong.oss-cn-shenzhen.aliyuncs.com/redis/%E5%88%86%E5%B8%83%E5%BC%8F%E9%94%81/clip_image042.jpg" srcset="/img/loading.gif" lazyload alt="img"></p>
<p> <strong>但是！</strong>上述情况只是解决了redis单点的情况。如果redis也是集群情况，会有数据同步的情况，以主从为例，如果setNX给到了主机，但是数据还未同步，然后主机宕机了，从机便又没有锁了，服务即又可以给redis从机加新的锁，这会导致锁失效。所以<strong>经典白学系列</strong>，解决问题的还得看下面两节。</p>
<h2 id="4-使用redisson-解决分布式锁"><a href="#4-使用redisson-解决分布式锁" class="headerlink" title="4 使用redisson 解决分布式锁"></a>4 使用redisson 解决分布式锁</h2><p>Github 地址：<a target="_blank" rel="noopener" href="https://github.com/redisson/redisson">https://github.com/redisson/redisson</a></p>
<p>Redisson是一个在Redis的基础上实现的Java驻内存数据网格（In-Memory Data Grid）。它不仅提供了一系列的分布式的Java常用对象，还提供了许多分布式服务。其中包括(BitSet, Set, Multimap, SortedSet, Map, List, Queue, BlockingQueue, Deque, BlockingDeque, Semaphore, Lock, AtomicLong, CountDownLatch, Publish / Subscribe, Bloom filter, Remote service, Spring cache, Executor service, Live Object service, Scheduler service) Redisson提供了使用Redis的最简单和最便捷的方法。Redisson的宗旨是促进使用者对Redis的关注分离（Separation of Concern），从而让使用者能够将精力更集中地放在处理业务逻辑上。</p>
<p><img src="https://lllong.oss-cn-shenzhen.aliyuncs.com/redis/%E5%88%86%E5%B8%83%E5%BC%8F%E9%94%81/clip_image043.png" srcset="/img/loading.gif" lazyload alt="1568176834908"></p>
<p>官方文档地址：<a target="_blank" rel="noopener" href="https://github.com/redisson/redisson/wiki">https://github.com/redisson/redisson/wiki</a></p>
<h3 id="4-1-实现代码"><a href="#4-1-实现代码" class="headerlink" title="4.1 实现代码"></a>4.1 实现代码</h3><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs xml"><span class="hljs-comment">&lt;!-- redisson 分布式锁--&gt;</span><br><span class="hljs-tag">&lt;<span class="hljs-name">dependency</span>&gt;</span><br>    <span class="hljs-tag">&lt;<span class="hljs-name">groupId</span>&gt;</span>org.redisson<span class="hljs-tag">&lt;/<span class="hljs-name">groupId</span>&gt;</span><br>    <span class="hljs-tag">&lt;<span class="hljs-name">artifactId</span>&gt;</span>redisson<span class="hljs-tag">&lt;/<span class="hljs-name">artifactId</span>&gt;</span><br>    <span class="hljs-tag">&lt;<span class="hljs-name">version</span>&gt;</span>3.11.2<span class="hljs-tag">&lt;/<span class="hljs-name">version</span>&gt;</span><br><span class="hljs-tag">&lt;/<span class="hljs-name">dependency</span>&gt;</span><br></code></pre></td></tr></table></figure>



<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">package</span> slx.blue.gmall.common.config;<br><br><span class="hljs-keyword">import</span> lombok.Data;<br><span class="hljs-keyword">import</span> org.redisson.Redisson;<br><span class="hljs-keyword">import</span> org.redisson.api.RedissonClient;<br><span class="hljs-keyword">import</span> org.redisson.config.Config;<br><span class="hljs-keyword">import</span> org.redisson.config.SingleServerConfig;<br><span class="hljs-keyword">import</span> org.springframework.boot.context.properties.ConfigurationProperties;<br><span class="hljs-keyword">import</span> org.springframework.context.annotation.Bean;<br><span class="hljs-keyword">import</span> org.springframework.context.annotation.Configuration;<br><span class="hljs-keyword">import</span> org.springframework.util.StringUtils;<br><br><span class="hljs-comment">/**</span><br><span class="hljs-comment"> * redisson配置信息</span><br><span class="hljs-comment"> */</span><br><span class="hljs-meta">@Data</span><br><span class="hljs-meta">@Configuration</span><br><span class="hljs-meta">@ConfigurationProperties(&quot;spring.redis&quot;)</span><br><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">RedissonConfig</span> </span>&#123;<br><br>    <span class="hljs-keyword">private</span> String host;<br><br>    <span class="hljs-keyword">private</span> String addresses;<br><br>    <span class="hljs-keyword">private</span> String password;<br><br>    <span class="hljs-keyword">private</span> String port;<br><br>    <span class="hljs-keyword">private</span> <span class="hljs-keyword">int</span> timeout = <span class="hljs-number">3000</span>;<br>    <span class="hljs-keyword">private</span> <span class="hljs-keyword">int</span> connectionPoolSize = <span class="hljs-number">64</span>;<br>    <span class="hljs-keyword">private</span> <span class="hljs-keyword">int</span> connectionMinimumIdleSize=<span class="hljs-number">10</span>;<br>    <span class="hljs-keyword">private</span> <span class="hljs-keyword">int</span> pingConnectionInterval = <span class="hljs-number">60000</span>;<br>    <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> String ADDRESS_PREFIX = <span class="hljs-string">&quot;redis://&quot;</span>;<br><br>    <span class="hljs-comment">/**</span><br><span class="hljs-comment">     * 自动装配</span><br><span class="hljs-comment">     *</span><br><span class="hljs-comment">     */</span><br>    <span class="hljs-meta">@Bean</span><br>    <span class="hljs-function">RedissonClient <span class="hljs-title">redissonSingle</span><span class="hljs-params">()</span> </span>&#123;<br>        Config config = <span class="hljs-keyword">new</span> Config();<br><br>        <span class="hljs-keyword">if</span>(StringUtils.isEmpty(host))&#123;<br>            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> RuntimeException(<span class="hljs-string">&quot;host is  empty&quot;</span>);<br>        &#125;<br>        SingleServerConfig serverConfig = config.useSingleServer()<br>                .setAddress(ADDRESS_PREFIX + <span class="hljs-keyword">this</span>.host + <span class="hljs-string">&quot;:&quot;</span> + port)<br>                .setTimeout(<span class="hljs-keyword">this</span>.timeout)<br>                .setPingConnectionInterval(pingConnectionInterval)<br>                .setConnectionPoolSize(<span class="hljs-keyword">this</span>.connectionPoolSize)<br>                .setConnectionMinimumIdleSize(<span class="hljs-keyword">this</span>.connectionMinimumIdleSize);<br>        <span class="hljs-keyword">if</span>(!StringUtils.isEmpty(<span class="hljs-keyword">this</span>.password)) &#123;<br>            serverConfig.setPassword(<span class="hljs-keyword">this</span>.password);<br>        &#125;<br>        <span class="hljs-keyword">return</span> Redisson.create(config);<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>

<h3 id="4-2-可重入锁（Reentrant-Lock）"><a href="#4-2-可重入锁（Reentrant-Lock）" class="headerlink" title="4.2 可重入锁（Reentrant Lock）"></a>4.2 可重入锁（Reentrant Lock）</h3><p>基于Redis的Redisson分布式可重入锁<code>RLock</code> Java对象实现了<code>java.util.concurrent.locks.Lock</code>接口。</p>
<p>大家都知道，如果负责储存这个分布式锁的Redisson节点宕机以后，而且这个锁正好处于锁住的状态时，这个锁会出现锁死的状态。为了避免这种情况的发生，Redisson内部提供了一个监控锁的看门狗，它的作用是在Redisson实例被关闭前，不断的延长锁的有效期。默认情况下，看门狗的检查锁的超时时间是30秒钟，也可以通过修改<code>Config.lockWatchdogTimeout</code>来另行指定。</p>
<p>另外Redisson还通过加锁的方法提供了<code>leaseTime</code>的参数来指定加锁的时间。超过这个时间后锁便自动解开了。</p>
<p>快速入门使用的就是可重入锁。也是最常使用的锁。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Autowired</span><br>   <span class="hljs-keyword">private</span> RedissonClient redissonClient;<br><br>   <span class="hljs-meta">@Override</span><br>   <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">setRedisRedisson</span><span class="hljs-params">()</span> </span>&#123;<br>       <span class="hljs-comment">//抢锁 extends Lock ：手动上锁释放锁</span><br>       RLock lock = redissonClient.getLock(<span class="hljs-string">&quot;lock&quot;</span>);<br>       <span class="hljs-comment">//操作</span><br>       <span class="hljs-keyword">try</span> &#123;<br>           <span class="hljs-comment">//抢锁 10秒内一直尝试加锁，加锁成功的话 锁的有效期为20秒</span><br>           <span class="hljs-keyword">if</span> (lock.tryLock(<span class="hljs-number">10</span>, <span class="hljs-number">20</span>, TimeUnit.SECONDS)) &#123;<br>               <span class="hljs-keyword">try</span> &#123;<br>                   <span class="hljs-comment">//尝试加锁成功</span><br>                   <span class="hljs-comment">//拿到锁了</span><br>                   <span class="hljs-comment">// 从缓存中拿到值</span><br>                   String value = stringRedisTemplate.boundValueOps(<span class="hljs-string">&quot;test01&quot;</span>).get();<br>                   <span class="hljs-comment">//如果为空 直接返回</span><br>                   <span class="hljs-keyword">if</span> (StringUtils.isEmpty(value)) &#123;<br>                       <span class="hljs-keyword">return</span>;<br>                   &#125;<br>                   <span class="hljs-comment">//不为空</span><br>                   Integer num = Integer.parseInt(value);<br>                   stringRedisTemplate.boundValueOps(<span class="hljs-string">&quot;test01&quot;</span>).set(++num + <span class="hljs-string">&quot;&quot;</span>);<br>                &#125; <span class="hljs-keyword">catch</span> (NumberFormatException e) &#123;<br>                   System.out.println(<span class="hljs-string">&quot;加锁成功以后出现异常&quot;</span>);<br>                   e.printStackTrace();<br>               &#125; <span class="hljs-keyword">finally</span> &#123;<br>                   <span class="hljs-comment">//释放锁</span><br>                   <span class="hljs-comment">//能进来 是肯定已经上了锁 所以应该在finally释放锁</span><br>                   lock.unlock();<br>               &#125;<br>           &#125;<br>       &#125; <span class="hljs-keyword">catch</span> (InterruptedException e) &#123;<br>           System.out.println(<span class="hljs-string">&quot;加锁失败 出现异常&quot;</span>);<br>           e.printStackTrace();<br>       &#125;<br>   &#125;<br></code></pre></td></tr></table></figure>

<h3 id="4-3-读写锁（ReadWriteLock）"><a href="#4-3-读写锁（ReadWriteLock）" class="headerlink" title="4.3 读写锁（ReadWriteLock）"></a>4.3 读写锁（ReadWriteLock）</h3><p>基于Redis的Redisson分布式可重入读写锁<code>RReadWriteLock</code> Java对象实现了<code>java.util.concurrent.locks.ReadWriteLock</code>接口。其中读锁和写锁都继承了RLock接口。</p>
<p>分布式可重入读写锁允许同时有多个读锁和一个写锁处于加锁状态。</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><code class="hljs java">RReadWriteLock rwlock = redisson.getReadWriteLock(<span class="hljs-string">&quot;anyRWLock&quot;</span>);<br><span class="hljs-comment">// 最常见的使用方法</span><br>rwlock.readLock().lock();<br><span class="hljs-comment">// 或</span><br>rwlock.writeLock().lock();<br><br><span class="hljs-comment">// 10秒钟以后自动解锁</span><br><span class="hljs-comment">// 无需调用unlock方法手动解锁</span><br>rwlock.readLock().lock(<span class="hljs-number">10</span>, TimeUnit.SECONDS);<br><span class="hljs-comment">// 或</span><br>rwlock.writeLock().lock(<span class="hljs-number">10</span>, TimeUnit.SECONDS);<br><br><span class="hljs-comment">// 尝试加锁，最多等待100秒，上锁以后10秒自动解锁</span><br><span class="hljs-keyword">boolean</span> res = rwlock.readLock().tryLock(<span class="hljs-number">100</span>, <span class="hljs-number">10</span>, TimeUnit.SECONDS);<br><span class="hljs-comment">// 或</span><br><span class="hljs-keyword">boolean</span> res = rwlock.writeLock().tryLock(<span class="hljs-number">100</span>, <span class="hljs-number">10</span>, TimeUnit.SECONDS);<br>...<br>lock.unlock();<br><br></code></pre></td></tr></table></figure>

<p>测试</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">/**</span><br><span class="hljs-comment">     * 读锁</span><br><span class="hljs-comment">     *</span><br><span class="hljs-comment">     * <span class="hljs-doctag">@return</span></span><br><span class="hljs-comment">     */</span><br>    <span class="hljs-meta">@Override</span><br>    <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">readLock</span><span class="hljs-params">()</span> </span>&#123;<br>        RReadWriteLock lock = redissonClient.getReadWriteLock(<span class="hljs-string">&quot;lock&quot;</span>);<br>        <span class="hljs-comment">//读锁:3秒</span><br>        lock.readLock().lock(<span class="hljs-number">10</span>, TimeUnit.SECONDS);<br>        <span class="hljs-comment">//redis读数据</span><br>        String s = stringRedisTemplate.boundValueOps(<span class="hljs-string">&quot;msg&quot;</span>).get();<br>        <span class="hljs-keyword">return</span> s;<br>    &#125;<br><br>    <span class="hljs-comment">/**</span><br><span class="hljs-comment">     * 写锁</span><br><span class="hljs-comment">     */</span><br>    <span class="hljs-meta">@Override</span><br>    <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">writeLock</span><span class="hljs-params">()</span> </span>&#123;<br>        RReadWriteLock lock = redissonClient.getReadWriteLock(<span class="hljs-string">&quot;lock&quot;</span>);<br>        <span class="hljs-comment">//写锁</span><br>        lock.writeLock().lock(<span class="hljs-number">10</span>, TimeUnit.SECONDS);<br>        <span class="hljs-comment">//写数据</span><br>        stringRedisTemplate.boundValueOps(<span class="hljs-string">&quot;msg&quot;</span>).set(UUID.randomUUID().toString());<br><br>        <span class="hljs-keyword">return</span> <span class="hljs-string">&quot;写完了&quot;</span>;<br>    &#125;<br></code></pre></td></tr></table></figure>

<p>打开开两个浏览器窗口测试：</p>
<p>localhost:8206/admin/product/test/read</p>
<p><a target="_blank" rel="noopener" href="http://localhost:8206/admin/product/test/write">http://localhost:8206/admin/product/test/write</a></p>
<p>- 同时访问写：一个写完之后，等待一会儿（约10s），另一个写开始</p>
<p>- 同时访问读：不用等待</p>
<p>- 先写后读：读要等待（约10s）写完成</p>
<p>- 先读后写：写要等待（约10s）读完成</p>
<h1 id="二-分布式锁改造获取sku信息"><a href="#二-分布式锁改造获取sku信息" class="headerlink" title="二 分布式锁改造获取sku信息"></a>二 分布式锁改造获取sku信息</h1><h3 id="3-1-使用redis方案"><a href="#3-1-使用redis方案" class="headerlink" title="3.1 使用redis方案"></a>3.1 使用redis方案</h3><p>RedisConst 类中追加一个变量</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">// 商品如果在数据库中不存在那么会缓存一个空对象进去，但是这个对象是没有用的，所以这个对象的过期时间应该不能太长，</span><br><span class="hljs-comment">// 如果太长会占用内存。</span><br><span class="hljs-comment">// 定义变量，记录空对象的缓存过期时间</span><br><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> <span class="hljs-keyword">long</span> SKUKEY_TEMPORARY_TIMEOUT = <span class="hljs-number">5</span> * <span class="hljs-number">60</span>;<br></code></pre></td></tr></table></figure>



<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Autowired</span><br><span class="hljs-keyword">private</span> StringRedisTemplate stringRedisTemplate;<br><br><span class="hljs-comment">/**</span><br><span class="hljs-comment"> * 通过redis分布式锁 获取sku的信息</span><br><span class="hljs-comment"> *</span><br><span class="hljs-comment"> * <span class="hljs-doctag">@param</span> skuId</span><br><span class="hljs-comment"> * <span class="hljs-doctag">@return</span></span><br><span class="hljs-comment"> */</span><br><span class="hljs-function"><span class="hljs-keyword">private</span> SkuInfo <span class="hljs-title">getSkuByRedis</span><span class="hljs-params">(Long skuId)</span> </span>&#123;<br>    <span class="hljs-comment">//参数校验</span><br>    <span class="hljs-keyword">if</span> (skuId == <span class="hljs-keyword">null</span>) &#123;<br>        <span class="hljs-keyword">return</span> <span class="hljs-keyword">null</span>;<br>    &#125;<br><br>    <span class="hljs-comment">//拿到商品key   sku:33:info</span><br>    String key = RedisConst.SKUKEY_PREFIX + skuId + RedisConst.SKUKEY_SUFFIX;<br>    <span class="hljs-comment">//根据key去查缓存值</span><br>    String s = (String) stringRedisTemplate.boundValueOps(key).get();<br>    <span class="hljs-comment">//检查值是否为空</span><br>    <span class="hljs-keyword">if</span> (!StringUtils.isEmpty(s)) &#123;<br>        <span class="hljs-comment">//缓存有数据，直接返回</span><br>        <span class="hljs-keyword">return</span> JSONObject.parseObject(s, SkuInfo.class);<br>    &#125; <span class="hljs-keyword">else</span> &#123;<br>        <span class="hljs-comment">//缓存没有数据</span><br>        <span class="hljs-comment">//定义当前商品的锁 sku:33:lock</span><br>        String lock = RedisConst.SKUKEY_PREFIX + skuId + RedisConst.SKULOCK_SUFFIX;<br>        <span class="hljs-comment">//随机uuid字符串 保证不删除被人的的锁</span><br>        String uuid = UUID.randomUUID().toString();<br>        <span class="hljs-comment">//加锁</span><br>        Boolean isLock = stringRedisTemplate.opsForValue().<br>                setIfAbsent(lock, uuid, RedisConst.SKULOCK_EXPIRE_PX2, TimeUnit.SECONDS);<br>        <span class="hljs-keyword">if</span> (isLock) &#123;<br>            <span class="hljs-comment">//加锁成功 从数据库拿到锁数据</span><br>            SkuInfo skuInfo = skuInfoMapper.selectById(skuId);<br>            <span class="hljs-keyword">if</span> (<span class="hljs-keyword">null</span> == skuInfo || skuInfo.getId() == <span class="hljs-keyword">null</span>) &#123;<br>                <span class="hljs-comment">//查到的数据为空 缓存三分钟空值 防止穿透</span><br>                skuInfo = <span class="hljs-keyword">new</span> SkuInfo();<br>                stringRedisTemplate.opsForValue()<br>                        .set(key, JSONObject.toJSONString(skuInfo),<br>                                RedisConst.SKUKEY_TEMPORARY_TIMEOUT, TimeUnit.SECONDS);<br>                <span class="hljs-keyword">return</span> skuInfo;<br>            &#125; <span class="hljs-keyword">else</span> &#123;<br>                <span class="hljs-comment">//不为空 设置缓存值 缓存时间为一天</span><br>                stringRedisTemplate.opsForValue()<br>                        .set(key, JSONObject.toJSONString(skuInfo),<br>                                RedisConst.SKUKEY_TIMEOUT, TimeUnit.SECONDS);<br>                <span class="hljs-comment">//设置lua释放锁的脚本</span><br>                DefaultRedisScript&lt;Long&gt; redisScript = <span class="hljs-keyword">new</span> DefaultRedisScript&lt;&gt;();<br>                <span class="hljs-comment">//设置释放脚本</span><br>                redisScript.setScriptText(<span class="hljs-string">&quot;if redis.call(&#x27;get&#x27;, KEYS[1]) == ARGV[1] then return redis.call(&#x27;del&#x27;, KEYS[1]) else return 0 end &quot;</span>);<br>                <span class="hljs-comment">//设置返回类型</span><br>                redisScript.setResultType(Long.class);<br>                <span class="hljs-comment">//释放锁</span><br>                stringRedisTemplate.execute(redisScript, Arrays.asList(lock), uuid);<br>                <span class="hljs-keyword">return</span> skuInfo;<br>            &#125;<br>        &#125; <span class="hljs-keyword">else</span> &#123;<br>            <span class="hljs-comment">//加锁失败 睡一秒 递归再次尝试</span><br>            <span class="hljs-keyword">try</span> &#123;<br>                TimeUnit.SECONDS.sleep(<span class="hljs-number">1</span>);<br>                <span class="hljs-keyword">return</span> getSkuByRedis(skuId);<br>            &#125; <span class="hljs-keyword">catch</span> (InterruptedException e) &#123;<br>                <span class="hljs-keyword">return</span> <span class="hljs-keyword">null</span>;<br>            &#125;<br>        &#125;<br>    &#125;<br>&#125;<br></code></pre></td></tr></table></figure>



<h3 id="3-2-使用redisson方案"><a href="#3-2-使用redisson方案" class="headerlink" title="3.2 使用redisson方案"></a>3.2 使用redisson方案</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@Autowired</span><br>    <span class="hljs-keyword">private</span> RedissonClient redissonClient;<br><br>    <span class="hljs-meta">@Autowired</span><br>    <span class="hljs-keyword">private</span> RedisTemplate redisTemplate;<br><br>    <span class="hljs-function"><span class="hljs-keyword">private</span> SkuInfo <span class="hljs-title">getSkuByRedisson</span><span class="hljs-params">(Long skuId)</span> </span>&#123;<br>        <span class="hljs-comment">//参数校验</span><br>        <span class="hljs-keyword">if</span> (skuId == <span class="hljs-keyword">null</span>) &#123;<br>            <span class="hljs-keyword">return</span> <span class="hljs-keyword">null</span>;<br>        &#125;<br>        <span class="hljs-comment">//定义key</span><br>        String key = RedisConst.SKUKEY_PREFIX + skuId + RedisConst.SKUKEY_SUFFIX;<br><br>        <span class="hljs-comment">//缓存取值</span><br>        SkuInfo skuInfo = (SkuInfo) redisTemplate.opsForValue().get(key);<br>        <span class="hljs-keyword">if</span> (<span class="hljs-keyword">null</span> != skuInfo) &#123;<br>            <span class="hljs-comment">//缓存有值 直接返回</span><br>            <span class="hljs-keyword">return</span> skuInfo;<br>        &#125;<br>        <span class="hljs-comment">//定义锁</span><br>        String lockKey = RedisConst.SKUKEY_PREFIX + skuId + RedisConst.SKULOCK_SUFFIX;<br>        <span class="hljs-comment">//拿到锁</span><br>        RLock rLock = redissonClient.getLock(lockKey);<br>        <span class="hljs-comment">//缓存为空 去数据库取值</span><br>        <span class="hljs-keyword">try</span> &#123;<br>            <span class="hljs-comment">//20秒内重复尝试去拿锁，拿到锁可以持有10秒</span><br>            <span class="hljs-keyword">if</span> (rLock.tryLock(<span class="hljs-number">20</span>, <span class="hljs-number">10</span>, TimeUnit.SECONDS)) &#123;<br>                <span class="hljs-comment">//进来了说明一定拿到锁了，所以在finally要释放锁</span><br>                <span class="hljs-keyword">try</span> &#123;<br>                    <span class="hljs-comment">//拿到锁了 执行逻辑</span><br>                    skuInfo = skuInfoMapper.selectById(skuId);<br>                    <span class="hljs-keyword">if</span> (<span class="hljs-keyword">null</span> == skuInfo || <span class="hljs-keyword">null</span> == skuInfo.getId()) &#123;<br>                        <span class="hljs-comment">//数据库查到的数据为空 缓存一个空值 三分钟</span><br>                        redisTemplate.opsForValue()<br>                                .set(key, skuInfo, RedisConst.SKUKEY_TEMPORARY_TIMEOUT, TimeUnit.SECONDS);<br>                    &#125; <span class="hljs-keyword">else</span> &#123;<br>                        <span class="hljs-comment">// 数据不为空 设置时间为一天的缓存</span><br>                        redisTemplate.opsForValue()<br>                                .set(key, skuInfo, RedisConst.SKUKEY_TIMEOUT, TimeUnit.SECONDS);<br>                    &#125;<br>                    <span class="hljs-keyword">return</span> skuInfo;<br>                &#125; <span class="hljs-keyword">catch</span> (Exception e) &#123;<br>                    e.printStackTrace();<br>                &#125; <span class="hljs-keyword">finally</span> &#123;<br>                    rLock.unlock();<br>                &#125;<br>            &#125; <span class="hljs-keyword">else</span> &#123;<br>                <span class="hljs-comment">//额定时间抢锁失败 递归重新执行</span><br>                <span class="hljs-keyword">try</span> &#123;<br>                    TimeUnit.SECONDS.sleep(<span class="hljs-number">1</span>);<br>                    getSkuByRedisson(skuId);<br>                &#125; <span class="hljs-keyword">catch</span> (InterruptedException e) &#123;<br>                &#125;<br><br>            &#125;<br>        &#125; <span class="hljs-keyword">catch</span> (InterruptedException e) &#123;<br><br>            e.printStackTrace();<br>        &#125;<br>        <span class="hljs-keyword">return</span> <span class="hljs-keyword">null</span>;<br><br>    &#125;<br></code></pre></td></tr></table></figure>



<h3 id="3-3-在getSkuInfo-中调用上述两个方法进行测试"><a href="#3-3-在getSkuInfo-中调用上述两个方法进行测试" class="headerlink" title="3.3 在getSkuInfo 中调用上述两个方法进行测试"></a>3.3 在getSkuInfo 中调用上述两个方法进行测试</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">/**</span><br><span class="hljs-comment">     * 查询sku的详细信息</span><br><span class="hljs-comment">     * <span class="hljs-doctag">@param</span> skuId</span><br><span class="hljs-comment">     * <span class="hljs-doctag">@return</span></span><br><span class="hljs-comment">     */</span><br>    <span class="hljs-meta">@Override</span><br>    <span class="hljs-function"><span class="hljs-keyword">public</span> SkuInfo <span class="hljs-title">getSkuInfo</span><span class="hljs-params">(Long skuId)</span> </span>&#123;<br>        <span class="hljs-comment">//redis或redisson两种方案任选其一</span><br><span class="hljs-comment">//        SkuInfo skuInfo = getSkuByRedis(skuId);</span><br>        SkuInfo skuInfo = getSkuByRedisson(skuId);<br>        <span class="hljs-keyword">if</span>(skuInfo == <span class="hljs-keyword">null</span> || skuInfo.getId() == <span class="hljs-keyword">null</span>)&#123;<br>            <span class="hljs-comment">//防止缓存宕机</span><br>            skuInfo = getSkuFromDb(skuId);<br>        &#125;<br>        <span class="hljs-comment">//返回结果</span><br>        <span class="hljs-keyword">return</span> skuInfo;<br>    &#125;<br></code></pre></td></tr></table></figure>

<h1 id="三-分布式锁-AOP实现缓存"><a href="#三-分布式锁-AOP实现缓存" class="headerlink" title="三 分布式锁 + AOP实现缓存"></a>三 分布式锁 + AOP实现缓存</h1><p>随着业务中缓存及分布式锁的加入，业务代码变的复杂起来，除了需要考虑业务逻辑本身，还要考虑缓存及分布式锁的问题，增加了程序员的工作量及开发难度。而缓存的玩法套路特别类似于事务，而声明式事务就是用了aop的思想实现的。</p>
<p><img src="https://lllong.oss-cn-shenzhen.aliyuncs.com/redis/%E5%88%86%E5%B8%83%E5%BC%8F%E9%94%81/clip_image047.png" srcset="/img/loading.gif" lazyload alt="20181207003231127"></p>
<ol>
<li><p>以 @Transactional 注解为植入点的切点，这样才能知道@Transactional注解标注的方法需要被代理。</p>
</li>
<li><p>@Transactional注解的切面逻辑类似于@Around</p>
</li>
</ol>
<p>模拟事务，缓存可以这样实现：</p>
<ol>
<li><p>自定义缓存注解@GmallCache（类似于事务@Transactional）</p>
</li>
<li><p>编写切面类，使用环绕通知实现缓存的逻辑封装</p>
</li>
</ol>
<p><img src="https://lllong.oss-cn-shenzhen.aliyuncs.com/redis/%E5%88%86%E5%B8%83%E5%BC%8F%E9%94%81/clip_image050.jpg" srcset="/img/loading.gif" lazyload alt="img"></p>
<h3 id="4-1-定义一个注解"><a href="#4-1-定义一个注解" class="headerlink" title="4.1 定义一个注解"></a>4.1 定义一个注解</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">package</span> slx.blue.gmall.common.cache;<br><br><br><span class="hljs-keyword">import</span> java.lang.annotation.*;<br><br><span class="hljs-meta">@Target(ElementType.METHOD)</span><br><span class="hljs-meta">@Retention(RetentionPolicy.RUNTIME)</span><br><span class="hljs-meta">@Documented</span><br><span class="hljs-keyword">public</span> <span class="hljs-meta">@interface</span> GmallCache &#123;<br>    <span class="hljs-function">String <span class="hljs-title">prefix</span><span class="hljs-params">()</span> <span class="hljs-keyword">default</span> &quot;cache&quot;</span>;<br>&#125;<br><br></code></pre></td></tr></table></figure>

<h3 id="4-2-定义一个切面类加上注解"><a href="#4-2-定义一个切面类加上注解" class="headerlink" title="4.2 定义一个切面类加上注解"></a>4.2 定义一个切面类加上注解</h3><p>Spring aop 参考文档：</p>
<p><a target="_blank" rel="noopener" href="https://docs.spring.io/spring/docs/5.2.6.BUILD-SNAPSHOT/spring-framework-reference/core.html#aop-api-pointcuts-aspectj">https://docs.spring.io/spring/docs/5.2.6.BUILD-SNAPSHOT/spring-framework-reference/core.html#aop-api-pointcuts-aspectj</a></p>
<p><img src="https://lllong.oss-cn-shenzhen.aliyuncs.com/redis/%E5%88%86%E5%B8%83%E5%BC%8F%E9%94%81/clip_image052.jpg" srcset="/img/loading.gif" lazyload alt="img"></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-keyword">package</span> slx.blue.gmall.common.cache;<br><br><span class="hljs-keyword">import</span> com.alibaba.fastjson.JSONObject;<br><span class="hljs-keyword">import</span> org.apache.commons.lang.StringUtils;<br><span class="hljs-keyword">import</span> org.aspectj.lang.ProceedingJoinPoint;<br><span class="hljs-keyword">import</span> org.aspectj.lang.annotation.Around;<br><span class="hljs-keyword">import</span> org.aspectj.lang.annotation.Aspect;<br><span class="hljs-keyword">import</span> org.aspectj.lang.reflect.MethodSignature;<br><span class="hljs-keyword">import</span> org.redisson.api.RLock;<br><span class="hljs-keyword">import</span> org.redisson.api.RedissonClient;<br><span class="hljs-keyword">import</span> org.springframework.beans.factory.annotation.Autowired;<br><span class="hljs-keyword">import</span> org.springframework.data.redis.core.RedisTemplate;<br><span class="hljs-keyword">import</span> org.springframework.stereotype.Component;<br><br><span class="hljs-keyword">import</span> java.util.Arrays;<br><span class="hljs-keyword">import</span> java.util.concurrent.TimeUnit;<br><br><span class="hljs-meta">@Component</span><br><span class="hljs-meta">@Aspect</span><br><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">GmallCacheAspect</span> </span>&#123;<br><br>    <span class="hljs-meta">@Autowired</span><br>    <span class="hljs-keyword">private</span> RedisTemplate redisTemplate;<br><br>    <span class="hljs-meta">@Autowired</span><br>    <span class="hljs-keyword">private</span> RedissonClient redissonClient;<br><br>    <span class="hljs-comment">//环绕监听的范围 只要加了范围内的注解 就可以AOP</span><br>    <span class="hljs-meta">@Around(&quot;@annotation(slx.blue.gmall.common.cache.GmallCache)&quot;)</span><br>    <span class="hljs-function"><span class="hljs-keyword">public</span> Object <span class="hljs-title">cacheAroundAdvice</span><span class="hljs-params">(ProceedingJoinPoint point)</span></span>&#123;<br><br>        <span class="hljs-comment">/*</span><br><span class="hljs-comment">        1.  获取参数列表</span><br><span class="hljs-comment">        2.  获取方法上的注解</span><br><span class="hljs-comment">        3.  获取前缀</span><br><span class="hljs-comment">        4.  获取目标方法的返回值</span><br><span class="hljs-comment">         */</span><br>        <span class="hljs-comment">//初始化通用返回结果</span><br>        Object result = <span class="hljs-keyword">null</span>;<br>        <span class="hljs-keyword">try</span> &#123;<br>            <span class="hljs-comment">//获取方法的参数 point：切点方法</span><br>            Object[] args = point.getArgs();<br>            System.out.println(<span class="hljs-string">&quot;gmallCache:&quot;</span>+args);<br>            <span class="hljs-comment">//拿到方法的签名：拿到方法的属性</span><br>            MethodSignature signature = (MethodSignature) point.getSignature();<br>            <span class="hljs-comment">//通过方法前面获取方法的注解 @GmallCache 然后实例化</span><br>            GmallCache gmallCache = signature.getMethod().getAnnotation(GmallCache.class);<br>            <span class="hljs-comment">// 拿到注解的前缀 用于缓存</span><br>            String prefix = gmallCache.prefix();<br>            <span class="hljs-comment">// 拼接缓存key getSkuInfo:[1,xxx]</span><br>            String key = prefix+Arrays.asList(args).toString();<br><br>            <span class="hljs-comment">// 私有方法，从redis获取值</span><br>            result = cacheHit(signature, key);<br>            <span class="hljs-keyword">if</span> (result!=<span class="hljs-keyword">null</span>)&#123;<br>                <span class="hljs-comment">// 缓存有数据 直接返回</span><br>                <span class="hljs-keyword">return</span> result;<br>            &#125;<br>            <span class="hljs-comment">/*----------------------方法还没开始运行---------------------------------*/</span><br>            <span class="hljs-comment">// 初始化分布式锁</span><br>            <span class="hljs-comment">// 锁的key getSkuInfo[10]:lock</span><br>            <span class="hljs-comment">// 缓存的key getSkuInfo[10]</span><br>            <span class="hljs-comment">//获取锁</span><br>            RLock lock = redissonClient.getLock(key + <span class="hljs-string">&quot;:lock&quot;</span>);<br>            <span class="hljs-comment">//尝试加锁 100内持续尝试获得锁 获得锁过期时间100秒</span><br>            <span class="hljs-keyword">boolean</span> flag = lock.tryLock(<span class="hljs-number">100</span>, <span class="hljs-number">100</span>, TimeUnit.SECONDS);<br>            <span class="hljs-comment">//获取锁成功</span><br>            <span class="hljs-keyword">if</span> (flag)&#123;<br>                <span class="hljs-comment">//加锁成功</span><br>               <span class="hljs-keyword">try</span> &#123;<br>                   <span class="hljs-keyword">try</span> &#123;<br>                       <span class="hljs-comment">//执行方法 查询数据库</span><br>                       result = point.proceed(point.getArgs());<br>                       <span class="hljs-comment">// 防止缓存穿透</span><br>                       <span class="hljs-keyword">if</span> (<span class="hljs-keyword">null</span>==result)&#123;<br>                           <span class="hljs-comment">// 数据库没有值，缓存一个空对象，给五分钟过期时间</span><br>                           Object o = <span class="hljs-keyword">new</span> Object();<br>                           <span class="hljs-keyword">this</span>.redisTemplate.opsForValue().set(key, JSONObject.toJSONString(o),<br>                                   <span class="hljs-number">300</span>,TimeUnit.SECONDS);<br>                           <span class="hljs-keyword">return</span> <span class="hljs-keyword">null</span>;<br>                       &#125;<span class="hljs-keyword">else</span> &#123;<br>                           <span class="hljs-comment">// 并把结果放入缓存 时间为一天</span><br>                           <span class="hljs-keyword">this</span>.redisTemplate.opsForValue().set(key, JSONObject.toJSONString(result),<br>                                   <span class="hljs-number">20</span>*<span class="hljs-number">60</span>*<span class="hljs-number">60</span>,TimeUnit.SECONDS);<br>                       &#125;<br>                   &#125; <span class="hljs-keyword">catch</span> (Throwable throwable) &#123;<br>                       throwable.printStackTrace();<br>                   &#125;<br><br>                   <span class="hljs-keyword">return</span> result;<br>               &#125;<span class="hljs-keyword">catch</span> (Exception e)&#123;<br>                   e.printStackTrace();<br>               &#125;<span class="hljs-keyword">finally</span> &#123;<br>                   <span class="hljs-comment">// 释放锁</span><br>                   lock.unlock();<br>               &#125;<br>            &#125;<br>        &#125;<span class="hljs-keyword">catch</span> (Exception e)&#123;<br>            e.printStackTrace();<br>        &#125;<br>        <span class="hljs-comment">//boolean flag = lock.tryLock(10L, 10L, TimeUnit.SECONDS);</span><br>        <span class="hljs-keyword">return</span> result;<br>    &#125;<br>    <span class="hljs-comment">// 根据key 获取缓存数据</span><br>    <span class="hljs-function"><span class="hljs-keyword">private</span> Object <span class="hljs-title">cacheHit</span><span class="hljs-params">(MethodSignature signature, String key)</span> </span>&#123;<br>        <span class="hljs-comment">// 1. 查询缓存</span><br>        String cache = (String)redisTemplate.opsForValue().get(key);<br>        <span class="hljs-keyword">if</span> (StringUtils.isNotBlank(cache)) &#123;<br>            <span class="hljs-comment">// 有，则反序列化，直接返回</span><br>            Class returnType = signature.getReturnType(); <span class="hljs-comment">// 获取方法返回类型</span><br>            <span class="hljs-comment">// 不能使用parseArray&lt;cache, T&gt;，因为不知道List&lt;T&gt;中的泛型</span><br>            <span class="hljs-keyword">return</span> JSONObject.parseObject(cache, returnType);<br>        &#125;<br>        <span class="hljs-keyword">return</span> <span class="hljs-keyword">null</span>;<br>    &#125;<br><br>&#125;<br><br></code></pre></td></tr></table></figure>

<h3 id="4-3-使用注解完成缓存"><a href="#4-3-使用注解完成缓存" class="headerlink" title="4.3 使用注解完成缓存"></a>4.3 使用注解完成缓存</h3><p>修改方法</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-comment">/**</span><br><span class="hljs-comment"> * 查询sku的详细信息</span><br><span class="hljs-comment"> *</span><br><span class="hljs-comment"> * <span class="hljs-doctag">@param</span> skuId</span><br><span class="hljs-comment"> * <span class="hljs-doctag">@return</span></span><br><span class="hljs-comment"> */</span><br><span class="hljs-meta">@GmallCache(prefix = RedisConst.SKUKEY_PREFIX)</span><br><span class="hljs-meta">@Override</span><br><span class="hljs-function"><span class="hljs-keyword">public</span> SkuInfo <span class="hljs-title">getSkuInfo</span><span class="hljs-params">(Long skuId)</span> </span>&#123;<br>    <span class="hljs-comment">//防止缓存宕机</span><br>    SkuInfo skuInfo = getSkuFromDb(skuId);<br>    <span class="hljs-comment">//返回结果</span><br>    <span class="hljs-keyword">return</span> skuInfo;<br>&#125;<br></code></pre></td></tr></table></figure>

<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><code class="hljs java"><span class="hljs-meta">@GmallCache(prefix = &quot;saleAttrValuesBySpu:&quot;)</span><br><span class="hljs-function"><span class="hljs-keyword">public</span> Map <span class="hljs-title">getSaleAttrValuesBySpu</span><span class="hljs-params">(Long spuId)</span></span><br><span class="hljs-function"></span><br><span class="hljs-function">@<span class="hljs-title">GmallCache</span><span class="hljs-params">(prefix = <span class="hljs-string">&quot;spuSaleAttrListCheckBySku:&quot;</span>)</span></span><br><span class="hljs-function"><span class="hljs-keyword">public</span> List&lt;SpuSaleAttr&gt; <span class="hljs-title">getSpuSaleAttrListCheckBySku</span><span class="hljs-params">(Long skuId, Long spuId)</span></span><br><span class="hljs-function"></span><br><span class="hljs-function">@<span class="hljs-title">GmallCache</span><span class="hljs-params">(prefix = <span class="hljs-string">&quot;skuPrice:&quot;</span>)</span></span><br><span class="hljs-function"><span class="hljs-keyword">public</span> BigDecimal <span class="hljs-title">getSkuPrice</span><span class="hljs-params">(Long skuId)</span> </span><br><span class="hljs-function"></span><br><span class="hljs-function">@<span class="hljs-title">GmallCache</span><span class="hljs-params">(prefix = <span class="hljs-string">&quot;categoryViewByCategory3Id:&quot;</span>)</span></span><br><span class="hljs-function"><span class="hljs-keyword">public</span> BaseCategoryView <span class="hljs-title">getCategoryViewByCategory3Id</span><span class="hljs-params">(Long category3Id)</span></span><br></code></pre></td></tr></table></figure>

<p>测试效果：</p>
<p><img src="https://lllong.oss-cn-shenzhen.aliyuncs.com/redis/%E5%88%86%E5%B8%83%E5%BC%8F%E9%94%81/clip_image054.jpg" srcset="/img/loading.gif" lazyload alt="img"></p>

            </div>
            <hr>
            <div>
              <div class="post-metas mb-3">
                
                  <div class="post-meta mr-3">
                    <i class="iconfont icon-category"></i>
                    
                      <a class="hover-with-bg" href="/categories/redis/">redis</a>
                    
                  </div>
                
                
                  <div class="post-meta">
                    <i class="iconfont icon-tags"></i>
                    
                      <a class="hover-with-bg" href="/tags/redis/">redis</a>
                    
                  </div>
                
              </div>
              
                <p class="note note-warning">
                  
                    本博客目前大部分文章都是参考尚硅谷或者马士兵教育的学习资料！<a href="http://www.atguigu.com/" rel="nofollow noopener"官网地址！</a> 
                  
                </p>
              
              
                <div class="post-prevnext">
                  <article class="post-prev col-6">
                    
                    
                  </article>
                  <article class="post-next col-6">
                    
                    
                      <a href="/2022/03/19/docker-fastdfs/">
                        <span class="hidden-mobile">Docker安装FastDfs和上传图片入门小程序</span>
                        <span class="visible-mobile">下一篇</span>
                        <i class="iconfont icon-arrowright"></i>
                      </a>
                    
                  </article>
                </div>
              
            </div>

            
              <!-- Comments -->
              <article class="comments" id="comments" lazyload>
                
                  
                
                

              </article>
            
          </article>
        </div>
      </div>
    </div>
    
      <div class="d-none d-lg-block col-lg-2 toc-container" id="toc-ctn">
        <div id="toc">
  <p class="toc-header"><i class="iconfont icon-list"></i>&nbsp;目录</p>
  <div class="toc-body" id="toc-body"></div>
</div>

      </div>
    
  </div>
</div>

<!-- Custom -->


    

    
      <a id="scroll-top-button" aria-label="TOP" href="#" role="button">
        <i class="iconfont icon-arrowup" aria-hidden="true"></i>
      </a>
    

    
      <div class="modal fade" id="modalSearch" tabindex="-1" role="dialog" aria-labelledby="ModalLabel"
     aria-hidden="true">
  <div class="modal-dialog modal-dialog-scrollable modal-lg" role="document">
    <div class="modal-content">
      <div class="modal-header text-center">
        <h4 class="modal-title w-100 font-weight-bold">搜索</h4>
        <button type="button" id="local-search-close" class="close" data-dismiss="modal" aria-label="Close">
          <span aria-hidden="true">&times;</span>
        </button>
      </div>
      <div class="modal-body mx-3">
        <div class="md-form mb-5">
          <input type="text" id="local-search-input" class="form-control validate">
          <label data-error="x" data-success="v"
                 for="local-search-input">关键词</label>
        </div>
        <div class="list-group" id="local-search-result"></div>
      </div>
    </div>
  </div>
</div>
    

    
  </main>

  <footer class="text-center mt-5 py-3">

  <div class="footer-content">
     <a href="https://hexo.io" target="_blank" rel="nofollow noopener"><span>Hexo</span></a> <i class="iconfont icon-love"></i> <a href="https://github.com/fluid-dev/hexo-theme-fluid" target="_blank" rel="nofollow noopener"><span>Fluid</span></a> 
	<!--《添加网站运行时间 -->
<br/>

<span id="timeDate">载入天数...</span><span id="times">载入时分秒...</span>
<script>
var now = new Date(); 

function createtime() {
    //此处修改你的建站时间或者网站上线时间
    var grt = new Date('11/02/2021 21:39:00');
    now.setTime(now.getTime() + 250);
    days = (now - grt) / 1000 / 60 / 60 / 24;

    dnum = Math.floor(days);
    hours = (now - grt) / 1000 / 60 / 60 - (24 * dnum);
    hnum = Math.floor(hours);
    if (String(hnum).length == 1) {
        hnum = "0" + hnum;
    }
    minutes = (now - grt) / 1000 / 60 - (24 * 60 * dnum) - (60 * hnum);
    mnum = Math.floor(minutes);
    if (String(mnum).length == 1) {
        mnum = "0" + mnum;
    }
    seconds = (now - grt) / 1000 - (24 * 60 * 60 * dnum) - (60 * 60 * hnum) - (60 * mnum);
    snum = Math.round(seconds);
    if (String(snum).length == 1) {
        snum = "0" + snum;
    }
    document.getElementById("timeDate").innerHTML = " 本站已各种夹缝中安全运行 " + dnum + " 天 ";
    document.getElementById("times").innerHTML = hnum + " 小时 " + mnum + " 分 " + snum + " 秒";
}
setInterval("createtime()", 250);
</script>

<!-- 添加网站运行时间》-->
  </div>
  
  <div class="statistics">
    
    

    
      
        <!-- 不蒜子统计PV -->
        <span id="busuanzi_container_site_pv" style="display: none">
            总访问量 
            <span id="busuanzi_value_site_pv"></span>
             次
          </span>
      
      
        <!-- 不蒜子统计UV -->
        <span id="busuanzi_container_site_uv" style="display: none">
            总访客数 
            <span id="busuanzi_value_site_uv"></span>
             人
          </span>
      
    
  </div>


  

  
</footer>


  <!-- SCRIPTS -->
  
  <script  src="https://cdn.jsdelivr.net/npm/nprogress@0/nprogress.min.js" ></script>
  <link  rel="stylesheet" href="https://cdn.jsdelivr.net/npm/nprogress@0/nprogress.min.css" />

  <script>
    NProgress.configure({"showSpinner":false,"trickleSpeed":100})
    NProgress.start()
    window.addEventListener('load', function() {
      NProgress.done();
    })
  </script>


<script  src="https://cdn.jsdelivr.net/npm/jquery@3/dist/jquery.min.js" ></script>
<script  src="https://cdn.jsdelivr.net/npm/bootstrap@4/dist/js/bootstrap.min.js" ></script>
<script  src="/js/events.js" ></script>
<script  src="/js/plugins.js" ></script>

<!-- Plugins -->


  <script  src="/js/local-search.js" ></script>



  
    <script  src="/js/img-lazyload.js" ></script>
  



  



  
    <script  src="https://cdn.jsdelivr.net/npm/tocbot@4/dist/tocbot.min.js" ></script>
  
  
    <script  src="https://cdn.jsdelivr.net/npm/@fancyapps/fancybox@3/dist/jquery.fancybox.min.js" ></script>
  
  
    <script  src="https://cdn.jsdelivr.net/npm/anchor-js@4/anchor.min.js" ></script>
  
  
    <script defer src="https://cdn.jsdelivr.net/npm/clipboard@2/dist/clipboard.min.js" ></script>
  



  <script defer src="https://busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js" ></script>




  <script  src="https://cdn.jsdelivr.net/npm/typed.js@2/lib/typed.min.js" ></script>
  <script>
    (function (window, document) {
      var typing = Fluid.plugins.typing;
      var title = document.getElementById('subtitle').title;
      
      typing(title)
      
    })(window, document);
  </script>















<!-- 主题的启动项 保持在最底部 -->
<script  src="/js/boot.js" ></script>


</body>
</html>
